﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace SLS.ExClassLib.MultiThreading.Extensions
{
    public static class EnumerableExtensions
    {
        /// <summary>
        /// Apply the given function to each element of the collection
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input">Input collection</param>
        /// <param name="action">Given function</param>
        public static void Iter<T>(this IEnumerable<T> input, Action<T> action)
        {
            foreach (T value in input)
                action(value);
        }

        /// <summary>
        /// Apply the given function to each element of the collection
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input">Input collection</param>
        /// <param name="action">Given function</param>
        /// <param name="exceptionHandler">Exception handler action</param>
        public static void IterSafe<T>(this IEnumerable<T> input, Action<T> action, Action<Exception> exceptionHandler)
        {
            foreach (T value in input)
            {
                try
                {
                    action(value);
                }
                catch (Exception ex)
                {
                    if (exceptionHandler != null)
                        exceptionHandler(ex);
                }
            }
        }

        /// <summary>
        /// Apply the given function to each element of the collection, doesn't throw exceptions
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input">Input collection</param>
        /// <param name="action">Given function</param>
        public static void IterSafe<T>(this IEnumerable<T> input, Action<T> action)
        {
            input.IterSafe(action, null);
        }

        /// <summary>
        /// Apply the given function to each element of the collection.
        /// The integer passed to the function indicates the index of element.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input">Input collection</param>
        /// <param name="action">Given function</param>
        public static void IterI<T>(this IEnumerable<T> input, Action<int, T> action)
        {
            int i = 0;
            foreach (T item in input)
                action(i++, item);
        }

        /// <summary>
        /// Max extension analog but returns object instead of <see cref="selector"/>  return value.
        /// </summary>
        public static TElement MaxElement<TElement, TData>(this IEnumerable<TElement> source,
                                                           Func<TElement, TData> selector)
            where TData : IComparable<TData>
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (selector == null)
                throw new ArgumentNullException("selector");
            Boolean firstElement = true;
            TElement result = default(TElement);
            TData maxValue = default(TData);
            foreach (TElement element in source)
            {
                TData candidate = selector(element);
                if (firstElement ||
                    (candidate.CompareTo(maxValue) > 0))
                {
                    firstElement = false;
                    maxValue = candidate;
                    result = element;
                }
            }
            return result;
        }

        public static bool IsNullOrEmpty<T>(this IEnumerable<T> value)
        {
            return value.IsNull() || value.Count() == 0;
        }

        public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int blockSize)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (blockSize <= 0)
                throw new ArgumentException("blockSize = {0}".FormatWith(blockSize), "blockSize");
            var result = new List<IEnumerable<T>>();
            for (int blockStartIndex = 0; blockStartIndex < source.Count(); blockStartIndex += blockSize)
            {
                int blockStart = blockStartIndex;
                int blockEnd = blockStartIndex + blockSize - 1;
                IEnumerable<T> block = source.Where((x, i) => i >= blockStart && i <= blockEnd);
                result.Add(block);
            }
            return result;
        }
    }
}